<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>手写Promise-class版</title>
  </head>
  <body>
    <script>
      // 手写promise实现步骤：1.构造函数 2.状态及原因 3.then方法 4.异步任务执行 5.链式调用

      const PENDING = "pending";
      const FULFILLED = "fulfilled";
      const REJECTED = "rejected";
      class myPromise {
        status = PENDING;
        result = undefined;

        #handler = []; // [{onFulfilled,onRejected}]
        constructor(fn) {
          const resolve = (result) => {
            if (this.status === PENDING) {
              this.status = FULFILLED;
              this.result = result;
              this.#handler.forEach(({ onFulfilled }) => {
                onFulfilled(this.result);
              });
            }
          };
          const reject = (result) => {
            if (this.status === PENDING) {
              this.status = REJECTED;
              this.result = result;
              this.#handler.forEach(({ onRejected }) => {
                onRejected(this.result);
              });
            }
          };
          try {
            fn(resolve, reject);
          } catch (error) {
            reject(error);
          }
        }
        then(onFulfilled, onRejected) {
          onFulfilled =
            typeof onFulfilled === "function" ? onFulfilled : (value) => value;
          onRejected =
            typeof onRejected === "function"
              ? onRejected
              : (reason) => {
                  throw reason;
                };

          const p2 = new myPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
              runAsyncTask(() => {
                try {
                  const val = onFulfilled(this.result);
                  //   console.log(val)
                  // TypeError: Chaining cycle detected for promise #<Promise>
                  // 处理重复引用
                  resolvePromise(val, p2, resolve, reject);
                  //   console.log("val",val)
                } catch (error) {
                  reject(error);
                }
              });
            } else if (this.status === REJECTED) {
              runAsyncTask(() => {
                try {
                  const val = onRejected(this.result);
                  resolvePromise(val, p2, resolve, reject);
                } catch (error) {
                  reject(error);
                }
              });
            } else if (this.status === PENDING) {
              this.#handler.push({
                onFulfilled: () => {
                  runAsyncTask(() => {
                    try {
                      const val = onFulfilled(this.result);
                      resolvePromise(val, p2, resolve, reject);
                    } catch (error) {
                      reject(error);
                    }
                  });
                },
                onRejected: () => {
                  runAsyncTask(() => {
                    try {
                      const val = onRejected(this.result);
                      resolvePromise(val, p2, resolve, reject);
                    } catch (error) {
                      reject(error);
                    }
                  });
                },
              });
            }
          });

          return p2;
        }

        catch(onRejected) {
          return this.then(undefined, onRejected);
        }

        finally(onFinallly) {
          return this.then(onFinallly, onFinallly);
        }
      }
      const p = new myPromise((resolve, reject) => {
        // reject("error")
        throw new Error("error");
      });

      const p1 = p
        .then((res) => {
          console.log("res");
        })
        .catch((err) => {
          console.log("error", err);
        })
        .finally(() => {
          console.log("finally");
        });

      // 用来执行异步的任务
      function runAsyncTask(callback) {
        setTimeout(() => {
          callback();
        }, 0);
      }

      function resolvePromise(val, p2, resolve, reject) {
        if (val === p2) {
          throw new TypeError("Chaining cycle detected for promise #<Promise>");
        }
        if (val instanceof myPromise) {
          val.then(
            (res) => {
              resolve(res);
            },
            (rej) => {
              reject(rej);
            }
          );
        } else {
          resolve(val);
        }
      }
    </script>
  </body>
</html>
